package com.google.android.apps.iosched.calendar;

import android.annotation.TargetApi;
import android.app.IntentService;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Intent;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.RemoteException;
import android.provider.CalendarContract;
import android.text.TextUtils;


import com.google.android.apps.iosched.R;
import com.google.android.apps.iosched.provider.ScheduleContract;
import com.google.android.apps.iosched.util.AccountUtils;
import com.google.android.apps.iosched.util.UIUtils;

import java.util.ArrayList;

import static com.google.android.apps.iosched.util.LogUtils.LOGE;
import static com.google.android.apps.iosched.util.LogUtils.LOGI;
import static com.google.android.apps.iosched.util.LogUtils.LOGW;
import static com.google.android.apps.iosched.util.LogUtils.makeLogTag;
/**
 *
 * Service：不是一个单独的进程，他和应用程序在一个进程中，但是Service又不是一个线程，
 * 所以我们应该避免在Service中进行耗时的操作，如果非要进行耗时操作就必须开启一个线程来进行
 * 那么IntentService 继承了 Service。
 * IntentService 使用队列的方式将请求的Intent加入队列，
 * 然后开启一个worker thread 来处理队列中得Intent，
 * 对于异步的startService请求，intentService 会根据队列的特性处理完第一个再处理第二个，
 * 这样每一个worker thread 都是独立的线程，不会阻塞主线程。这样我们就可以在Intent service中进行比较耗时的操作了
 * Created by Alex on 13-12-31.
 */
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public class SessionCalendarService extends IntentService {

    private static final String TAG = makeLogTag(SessionCalendarService.class);

    public static final String ACTION_ADD_SESSION_CALENDAR =
            "com.google.android.apps.iosched.action.ADD_SESSION_CALENDAR";
    public static final String ACTION_REMOVE_SESSION_CALENDAR =
            "com.google.android.apps.iosched.action.REMOVE_SESSION_CALENDAR";
    public static final String ACTION_UPDATE_ALL_SESSIONS_CALENDAR =
            "com.google.android.apps.iosched.action.UPDATE_ALL_SESSIONS_CALENDAR";
    public static final String ACTION_UPDATE_ALL_SESSIONS_CALENDAR_COMPLETED =
            "com.google.android.apps.iosched.action.UPDATE_CALENDAR_COMPLETED";
    public static final String ACTION_CLEAR_ALL_SESSIONS_CALENDAR =
            "com.google.android.apps.iosched.action.CLEAR_ALL_SESSIONS_CALENDAR";
    public static final String EXTRA_ACCOUNT_NAME =
            "com.google.android.apps.iosched.extra.ACCOUNT_NAME";
    public static final String EXTRA_SESSION_BLOCK_START =
            "com.google.android.apps.iosched.extra.SESSION_BLOCK_START";
    public static final String EXTRA_SESSION_BLOCK_END =
            "com.google.android.apps.iosched.extra.SESSION_BLOCK_END";
    public static final String EXTRA_SESSION_TITLE =
            "com.google.android.apps.iosched.extra.SESSION_TITLE";
    public static final String EXTRA_SESSION_ROOM =
            "com.google.android.apps.iosched.extra.SESSION_ROOM";

    private static final long INVALID_CALENDAR_ID = -1;

    private static final String CALENDAR_CLEAR_SEARCH_LIKE_EXPRESSION = "%added by Google I/O 2012%";

    public SessionCalendarService(String name) {
        super(TAG);
    }

    @Override
    protected void onHandleIntent(Intent intent) {
        final String action  = intent.getAction();

        final ContentResolver resolver = getContentResolver();
        boolean isAddEvent = false;

        if (ACTION_ADD_SESSION_CALENDAR.equals(action)){
            isAddEvent = true;
        } else if (ACTION_REMOVE_SESSION_CALENDAR.equals(action)){
            isAddEvent = false;
        } else if (ACTION_UPDATE_ALL_SESSIONS_CALENDAR.equals(action)){
            try {
                getContentResolver().applyBatch(CalendarContract.AUTHORITY,processAllSessionsCalendar(resolver,getCalendarId(intent)));
                sendBroadcast(new Intent(SessionCalendarService.ACTION_UPDATE_ALL_SESSIONS_CALENDAR_COMPLETED));
            }catch (RemoteException e){
                LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
            }catch (OperationApplicationException e){
                LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
            }
        }else if(ACTION_CLEAR_ALL_SESSIONS_CALENDAR.equals(action)){
            try {
                getContentResolver().applyBatch(CalendarContract.AUTHORITY, processClearAllSessions(resolver, getCalendarId(intent)));

            }catch (RemoteException e){
                LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
            }catch (OperationApplicationException e){
                LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
            }
        }else {
            return;
        }

        final Uri uri = intent.getData();
        final Bundle extras = intent.getExtras();
        if (uri == null || extras == null){
            return ;
        }

        try {
            resolver.applyBatch(CalendarContract.AUTHORITY,
                    processSessionCalendar(resolver,getCalendarId(intent),isAddEvent,uri,
                            extras.getLong(EXTRA_SESSION_BLOCK_START),
                            extras.getLong(EXTRA_SESSION_BLOCK_END),
                            extras.getString(EXTRA_SESSION_TITLE),
                            extras.getString(EXTRA_SESSION_ROOM)
                            )
                    );
        }catch (RemoteException e){
            LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
        }catch (OperationApplicationException e){
            LOGE(TAG,"Error clearing all sessions from Google Calendar",e);
        }
    }

    /**
     * 获取当前用户在谷歌日历上的意图包含的信息
     * @param intent
     * @return
     */
    private long getCalendarId(Intent intent){
        final String accountName;
        if (intent != null && intent.hasExtra(EXTRA_ACCOUNT_NAME)){
            accountName = intent.getStringExtra(EXTRA_ACCOUNT_NAME);
        }else{
            accountName = AccountUtils.getChosenAccountName(this);
        }

        if (TextUtils.isEmpty(accountName)){
            return INVALID_CALENDAR_ID;
        }

        Cursor calendarsCursor = getContentResolver().query(CalendarContract.Calendars.CONTENT_URI, new String[]{"_id"},
                "account name = ownerAccount and account name = ?", new String[]{accountName}, null
        );

        long calendarId = INVALID_CALENDAR_ID;
        if (calendarsCursor != null && calendarsCursor.moveToFirst()){
            calendarId = calendarsCursor.getLong(0);
            calendarsCursor.close();
        }

        return calendarId;

    }

    private String makeCalendarEventTitle(String sessionTitle){
        return sessionTitle + getResources().getString(R.string.session_calendar_suffix);
    }

    private ArrayList<ContentProviderOperation> processAllSessionsCalendar(ContentResolver resolver,final long calendarId){

        ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();

        if (calendarId == INVALID_CALENDAR_ID){
            return batch;
        }

        Cursor cursor = resolver.query(ScheduleContract.Sessions.CONTENT_URI, SessionsQuery.PROJECTION, null, null, null);
        if (cursor != null){
            while (cursor.moveToNext()) {
                Uri uri = ScheduleContract.Sessions.buildSessionUri(Long.valueOf(cursor.getLong(0)).toString());
                boolean isAddEvent = (cursor.getInt(SessionsQuery.SESSION_STARRED) == 1);
                if (isAddEvent){
                    batch.addAll(processSessionCalendar(resolver, calendarId, isAddEvent, uri,
                            cursor.getLong(SessionsQuery.BLOCK_START),
                            cursor.getLong(SessionsQuery.BLOCK_END),
                            cursor.getString(SessionsQuery.SESSION_TITLE),
                            cursor.getString(SessionsQuery.ROOM_NAME)
                    ));
                }
            }
            cursor.close();
        }
        return batch;

    }
    
    private ArrayList<ContentProviderOperation> processSessionCalendar(
            final ContentResolver resolver,
            final long calendarId,
            final boolean isAddEvent,
            final Uri sessionUri,
            final long sessionBlockStart,
            final long sessionBlockEnd,
            final String sessionTitle,
            final String sessionRoom
    ){

        ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();

        if (calendarId == INVALID_CALENDAR_ID){
            return batch;
        }

        final String calendarEventTitle = makeCalendarEventTitle(sessionTitle);

        Cursor cursor;
        ContentValues values = new ContentValues();

        if (isAddEvent){
            if (sessionBlockStart == 0L || sessionBlockEnd == 0l || sessionTitle == null){
                LOGW(TAG,"Unable to add aCalendar event due to insufficient input parameters.");
                return batch;
            }

            cursor = resolver.query(
                    CalendarContract.Events.CONTENT_URI,
                    new String[]{
                            CalendarContract.Events._ID
                    },
                    CalendarContract.Events.CALENDAR_ID + "=? and "
                        +CalendarContract.Events.TITLE + "=? and "
                        +CalendarContract.Events.DTSTART +">=? and "
                        +CalendarContract.Events.DELETED + "<=?",
                    new String[]{
                            Long.valueOf(calendarId).toString(),
                            calendarEventTitle,
                            Long.toString(UIUtils.CONFERENCE_START_MILLIS),
                            Long.toString(UIUtils.CONFERENCE_END_MILLIS)
                    },
                    null
                  );

            long newEventId = -1;

            if (cursor != null && cursor.moveToFirst()){
                newEventId = cursor.getLong(0);
                cursor.close();
            }else {
                values.clear();
                values.put(CalendarContract.Events.DTSTART, sessionBlockStart);
                values.put(CalendarContract.Events.DTEND, sessionBlockEnd);
                values.put(CalendarContract.Events.EVENT_LOCATION, sessionRoom);
                values.put(CalendarContract.Events.TITLE, calendarEventTitle);
                values.put(CalendarContract.Events.CALENDAR_ID, calendarId);
                values.put(CalendarContract.Events.EVENT_TIMEZONE,UIUtils.CONFERNCE_TIME_ZONE.getID());

                Uri eventUri = resolver.insert(CalendarContract.Events.CONTENT_URI,values);
                String eventId = eventUri.getLastPathSegment();
                if (eventId == null){
                    return batch;
                }

                newEventId = Long.valueOf(eventId);

            }
            values.clear();
            values.put(ScheduleContract.Sessions.SESSION_CAL_EVENT_ID,newEventId);
            resolver.update(sessionUri,values,null,null);
        }else{
            cursor = resolver.query(sessionUri,new String[]{ScheduleContract.Sessions.SESSION_CAL_EVENT_ID},
                    null,null,null
                    );
            long calendarEventId = -1;
            if (cursor != null && cursor.moveToFirst()){
                calendarEventId=cursor.getLong(0);
                cursor.close();;
            }

            int affectedRows = 0;
            if (calendarEventId != -1){
                affectedRows = resolver.delete(CalendarContract.Events.CONTENT_URI,CalendarContract.Events._ID + "=?",
                        new String[]{Long.valueOf(calendarEventId).toString()}
                        );
            }

            if (affectedRows ==0){
                resolver.delete(CalendarContract.Events.CONTENT_URI,
                        String.format("%s=? and %s=? and %s=? and %s=?",
                                CalendarContract.Events.CALENDAR_ID,
                                CalendarContract.Events.TITLE,
                                CalendarContract.Events.DTSTART,
                                CalendarContract.Events.DTEND
                                ),
                        new String[]{
                                Long.valueOf(calendarId).toString(),
                                calendarEventTitle,
                                Long.valueOf(sessionBlockStart).toString(),
                                Long.valueOf(sessionBlockEnd).toString()
                        }
                        );
            }

            values.clear();
            values.put(ScheduleContract.Sessions.SESSION_CAL_EVENT_ID,(Long)null);
            resolver.update(sessionUri,values,null,null);
        }

        return batch;
    }

    private ArrayList<ContentProviderOperation> processClearAllSessions(ContentResolver resolver, long calendarId) {

        ArrayList<ContentProviderOperation> batch = new ArrayList<ContentProviderOperation>();

        if (calendarId == INVALID_CALENDAR_ID){
            return batch;
        }

        batch.add(
                ContentProviderOperation.newDelete(CalendarContract.Events.CONTENT_URI)
                       .withSelection(
                            CalendarContract.Events.CALENDAR_ID + " = ? and "
                               +CalendarContract.Events.TITLE  + " LIKE ? and "
                               +CalendarContract.Events.DTSTART + " >=? and "
                               +CalendarContract.Events.DTEND + " <=? ",
                            new String[]{
                                Long.toString(calendarId),
                                    CALENDAR_CLEAR_SEARCH_LIKE_EXPRESSION,
                                    Long.toString(UIUtils.CONFERENCE_START_MILLIS),
                                    Long.toString(UIUtils.CONFERENCE_END_MILLIS)
                            }).build()
        );

        return batch;
    }

    private interface SessionsQuery{
        String[] PROJECTION = {
                ScheduleContract.Sessions._ID,
                ScheduleContract.Sessions.BLOCK_START,
                ScheduleContract.Sessions.BLOCK_END,
                ScheduleContract.Sessions.SESSION_TITLE,
                ScheduleContract.Sessions.ROOM_NAME,
                ScheduleContract.Sessions.SESSION_STARRED,
        };

        int _ID = 0;
        int BLOCK_START = 1;
        int BLOCK_END = 2;
        int SESSION_TITLE = 3;
        int ROOM_NAME = 4;
        int SESSION_STARRED = 5;

    }
}
